#ifndef _CANARY2_CORO_TASK_H_
#define _CANARY2_CORO_TASK_H_

#include <coroutine>
#include <memory>
#include <cassert>
#include <atomic>
#include <mutex>
#include <cstddef>
#include <string>
#include <fmt/format.h>

#include "awaker_concepts.h"
#include "task_concepts.h"
#include "normal_awaker.h"
#include "return_value_or_void.h"
#include "data_propagator.h"
#include "promise_base.h"
#include "task_concepts.h"

#include "../sys/resumable.h"
#include "../sys/scheduler_concepts.h"
#include "../util/coro_error.h"
#include "../util/assert.h"
#include "../util/logger.h"

namespace wilson::coro
{
    template<typename T>
    struct _task
    {
        struct [[nodiscard]] _type;
    };

    template<typename T> 
    class get_value_aw
    {
    public:
        using value_type = T;
        using promise_type = 
            typename _task<T>::_type::promise_type;

    public:
        get_value_aw(typename _task<T>::_type& producer_tsk) 
            : m_data_propagator{ producer_tsk.promise().data_propagator() }
        {
        }

        bool await_ready() const 
        { 
            return m_data_propagator->ready();
        }

        void await_suspend(::std::coroutine_handle<> consumer_h) 
        {
            m_data_propagator->set_awaker(normal_awaker{ consumer_h });
        }

        auto await_resume() noexcept
        { 
            if constexpr (!::std::same_as<void, value_type>)
                return m_data_propagator->template retrive_value<value_type>();
            else return;
        }

    private:
        ::std::shared_ptr<data_propagator> m_data_propagator;
    };

    // can return T value
    template<typename T>
    class _task<T>::_type
    {
    public:
        friend class get_value_aw<T>;
        using value_type = T;

    public:
        class valued_promise 
            : public return_value_or_void<T, normal_awaker>,
              public promise_base
        {
        public:
            _task<T>::_type get_return_object() noexcept
            { 
                return { *this };
            }

            void unhandled_exception() const
            {
                throw;
            }
        };

        using promise_type = valued_promise;

    public:
        _type(promise_type& p) noexcept
            : t_coro_handle { 
                ::std::coroutine_handle<promise_type>
                    ::from_promise(p) 
              }
        {
        }

        _type(::std::coroutine_handle<promise_type> h) noexcept
            : t_coro_handle{ h }
        {
        }

        _type(_type&& other)
            : t_coro_handle{ other.t_coro_handle }
        {
            other.t_coro_handle = nullptr;
        }

        ~_type()
        {
            if (t_coro_handle && t_coro_handle.done())
            {
                t_coro_handle.destroy();
                WILSON_LOG_DEBUG("~task<T>()");
            }
        }

        get_value_aw<T> get() noexcept 
        { 
            t_coro_handle.promise().set_need_return();
            return { *this }; 
        }

        void resume() { t_coro_handle.resume(); }

        operator wilson::sys::resumable() &&
        { 
            auto coro_h = t_coro_handle;
            t_coro_handle = nullptr;
            return { 
                static_cast<::std::coroutine_handle<>>(
                    coro_h) 
            }; 
        }

    private:
        auto& promise() noexcept { return t_coro_handle.promise(); }
        auto  coro_handle() noexcept { return t_coro_handle; }
        void* promise_addr() noexcept { return reinterpret_cast<void*>(&promise()); }

    private:
        ::std::coroutine_handle<promise_type> t_coro_handle;   
    }; // class _task<T>::_type
    
    template<>
    class _task<void>::_type
    {
    public:
        using value_type = void;
        friend class get_value_aw<void>;

    public:
        class void_promise
            : public return_value_or_void<void, normal_awaker>,
              public promise_base
        {
        public:
            _task<void>::_type get_return_object() noexcept
            { 
                return { *this }; 
            }

            void unhandled_exception() const
            {
                throw;
            }
        };

        using promise_type = void_promise;

    public:
        _type(promise_type& p) noexcept
            : tv_coro_handle {
                ::std::coroutine_handle<promise_type>
                    ::from_promise(p)
              }
        {
        }
        
        _type(::std::coroutine_handle<promise_type> h) noexcept
            : tv_coro_handle{ h }
        {
        }

        _type(_type&& other)
            : tv_coro_handle{ other.tv_coro_handle }
        {
            other.tv_coro_handle = nullptr;
        }

        ~_type()
        {
            if (tv_coro_handle && tv_coro_handle.done())
            {
                tv_coro_handle.destroy();
                WILSON_LOG_DEBUG("~task<void>()");
            }
        }

        void resume() { tv_coro_handle.resume(); }
        get_value_aw<void> get() noexcept 
        { 
            tv_coro_handle.promise().set_need_return();
            return { *this }; 
        }

        operator wilson::sys::resumable() &&
        { 
            auto coro_h = tv_coro_handle;
            tv_coro_handle = nullptr;
            return { 
                static_cast<::std::coroutine_handle<>>(
                    coro_h) 
            }; 
        }

    private:
        auto& promise() noexcept { return tv_coro_handle.promise(); }
        auto  coro_handle() noexcept { return tv_coro_handle; }
        auto* promise_addr() noexcept { return reinterpret_cast<void*>(&promise()); }

    private:
        ::std::coroutine_handle<promise_type> tv_coro_handle;
    }; // class _task<void>::_type


    template<typename T>
    using task = typename _task<T>::_type;

    template<sys::scheduler Schr_Or_TM, as_task Task>
    [[nodiscard("schedule_aw return an awaitable object")]]
    auto schedule_aw(Schr_Or_TM& sot, Task&& corot)
    {
        auto aw = corot.get();
        sys::schedule(sot, ::std::forward<Task>(corot));
        return aw;
    }

} // namespace wilson::coro

#endif
